package com.sunwayworld.basemodule.business.result.computing.formula;

import com.sunwayworld.basemodule.business.result.bean.LimsResultBean;
import com.sunwayworld.basemodule.business.result.bean.LimsResultDTO;
import com.sunwayworld.basemodule.business.result.computing.OrderTaskResultComputingEngine;
import com.sunwayworld.basemodule.samplemanage.sample.bean.LimsSampleBean;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.exception.FormulaException;
import com.sunwayworld.framework.formula.GikamComputingDataHolder;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.utils.*;
import com.sunwayworld.module.sys.code.bean.CoreCodeBean;
import com.sunwayworld.module.sys.code.service.CoreCodeService;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.OptionalDouble;
import java.util.stream.Collectors;

public class ResultFormulaRepository {
    /**
     * 获取当前处理中的样品结果
     */
    public static LimsResultDTO getCurrent() {
        return GikamComputingDataHolder.getCurrentData();
    }

    /**
     * 获取一个样品、一个测试、一个分析项下的样品结果（匹配多条时选择一个）<br>
     * args[0]：分析项名称（必填）<br>
     * args[1]：测试编码（选填，默认是当前样品结果的测试编码）<br>
     * args[2]：样品编号（选填，默认是当前样品结果的样品编号）
     */
    public static LimsResultDTO get(String... args) {
        List<LimsResultDTO> taskResultList = getTaskResultList(args);

        if (taskResultList.isEmpty()) {
            return new LimsResultDTO();
        } else {
            return taskResultList.get(0);
        }
    }

    // 获取 当前样本 某检测项的结果 String
    public static String RS(String item) {
        LimsResultDTO resultBean = getTaskResultList(item);
        Object propertyValue = BeanUtils.getPropertyValue(resultBean, "originalResult");
        return propertyValue == null ? null : propertyValue.toString();
    }

    // 获取 当前样本 某检测项的结果 double
    public static Double RN(String item) {
        LimsResultDTO resultBean = getTaskResultList(item);
        return parseDouble(BeanUtils.getPropertyValue(resultBean, "originalResult"));
    }

    // 去掉非数字，然后转double
    private static Double parseDouble(String txt) {
        if(NumberUtils.isNumber(txt)){
            return NumberUtils.parseDouble(txt);
        }
        return 0d;
    }

    // 获取 当前样本的属性值 字符串
    public static String SS(String propertyName){
        LimsResultDTO taskResult = GikamComputingDataHolder.getCurrentData();
        List<LimsSampleBean> sampleBeanList = GikamComputingDataHolder.get(OrderTaskResultComputingEngine.FOLDER_SAMPLE_LIST);
        LimsSampleBean sampleBean = sampleBeanList.stream()
                .filter(r -> ObjectUtils.equals(r.getSampleCode(), taskResult.getSampleCode()))
                .findFirst().orElse(null);

        Object propertyValue = BeanUtils.getPropertyValue(sampleBean, propertyName);
        return propertyValue == null ? null : propertyValue.toString();
    }

    // 获取 当前样本的属性值 数值
    public static Double SN(String propertyName){
        LimsResultDTO taskResult = GikamComputingDataHolder.getCurrentData();
        List<LimsSampleBean> sampleBeanList = GikamComputingDataHolder.get(OrderTaskResultComputingEngine.FOLDER_SAMPLE_LIST);
        LimsSampleBean sampleBean = sampleBeanList.stream()
                .filter(r -> ObjectUtils.equals(r.getSampleCode(), taskResult.getSampleCode()))
                .findFirst().orElse(null);

        return parseDouble(BeanUtils.getPropertyValue(sampleBean, propertyName));
    }

    // 获取 当前样本的属性值 数值
    public static LocalDateTime ST(String propertyName){
        LimsResultDTO taskResult = GikamComputingDataHolder.getCurrentData();
        List<LimsSampleBean> sampleBeanList = GikamComputingDataHolder.get(OrderTaskResultComputingEngine.FOLDER_SAMPLE_LIST);
        LimsSampleBean sampleBean = sampleBeanList.stream()
                .filter(r -> ObjectUtils.equals(r.getSampleCode(), taskResult.getSampleCode()))
                .findFirst().orElse(null);
        return BeanUtils.getPropertyValue(sampleBean, propertyName);
    }

    public static LocalDateTime TDD(LocalDateTime time, String day) {
        return time.plusDays(Integer.parseInt(day));
    }

    public static String TF(LocalDateTime time, String formatter) {
        return DateTimeUtils.formatLocalDateTime(time, formatter);
    }

    public static String sub(String s, String st, String e){
        int start = Integer.parseInt(st);
        int end = Integer.parseInt(e);
        return s.substring(start, end);
    }

    public static String subEnd(String s, String st){
        int start = Integer.parseInt(st);
        if(s.length() < start) return null;
        return s.substring(s.length() - start);
    }

    public static String subEnd(String s, String st, String e){
        int start = Integer.parseInt(st);
        int end = Integer.parseInt(e);
        if(s.length() < start) return null;
        return s.substring(s.length() - end, s.length() - start);
    }

    public static String CC(String codeCategoryId, String code) {
        CoreCodeService coreCodeService = ApplicationContextHelper.getBean(CoreCodeService.class);
        CoreCodeBean coreCodeBean = coreCodeService.selectFirstByFilter(SearchFilter.instance()
                .match("codeCategoryId", codeCategoryId).filter(MatchPattern.EQ)
                .match("code", code).filter(MatchPattern.EQ));
        return coreCodeBean == null? code: coreCodeBean.getCodeName();
    }

    // 除法
    public static Double div(String a, String b) {
        return ArithUtils.div(a, b);
    }

    public static boolean NN(Object prop){
        return Objects.isNull(prop);
    }

    public static boolean eq(String a, String b){
        return a.equals(b);
    }

    public static boolean eql(String a, String b){
        return a.equalsIgnoreCase(b);
    }

    public static boolean ct(String a, String b){
        return StringUtils.contains(a, b);
    }

    public static boolean ctl(String a, String b){
        return StringUtils.containsIgnoreCase(a, b);
    }

    public static double ln(Double a){
        return Math.log(a);
    }

    public static double lg(Double a){
        return Math.log10(a);
    }

    /**
     * 把数据修约之后的结果返回
     *
     * @param roundtype
     *            修约类型，必输项。
     * @param picture
     *            修约长度，必输项。
     * @param value
     *            要修约的数，必输项。
     */
    public static String getRounding(String roundtype, String picture, String value) {
        return NumberUtils.rounding(value, roundtype + "," + picture);
    }

    // 计算最小值
    public static Double min(String... args) {
        if(args == null) {
            return null;
        }
        List<String> list = Arrays.asList(args);
        OptionalDouble min = list.stream().mapToDouble(ResultFormulaRepository::parseDouble).min();
        return min.orElse(0.0);
    }

    // 计算最大值
    public static Double max(String... args) {
        if(args == null) {
            return null;
        }
        List<String> list = Arrays.asList(args);
        OptionalDouble max = list.stream().mapToDouble(ResultFormulaRepository::parseDouble).max();
        return max.orElse(0.0);
    }

    // 计算平均值
    public static Double avg(String... args) {
        if(args == null) {
            return null;
        }
        List<String> list = Arrays.asList(args);
        OptionalDouble average = list.stream().mapToDouble(ResultFormulaRepository::parseDouble).average();
        return average.orElse(0.0);
    }

    // 计算和
    public static Double sum(String... args) {
        if(args == null) {
            return null;
        }
        List<String> list = Arrays.asList(args);
        return list.stream().mapToDouble(ResultFormulaRepository::parseDouble).sum();
    }

    // 保留2位小数
    public static Double ps(Double a) {
        BigDecimal two = new BigDecimal(a);
        return two.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    // 求平方差
    public static Double evp(String... args) {
        if(args == null) {
            return null;
        }
        Double avg = avg(args);
        List<String> list = Arrays.asList(args);
        double sum = list.stream().map(ResultFormulaRepository::parseDouble).map(t -> (t - avg) * (t - avg)).mapToDouble(t -> t).sum();
        return ps(sum/list.size());
    }

    // -----------------------------------------------------------------------
    // 私有方法
    // -----------------------------------------------------------------------
    private static List<LimsResultDTO> getTaskResultList(String... args) {
        if (args.length == 0) {
            throw new FormulaException("MODULE.BUSINESS.RESULT.TIP.FORMULA_REQUIRE_ITEMNAME");
        }

        if (StringUtils.isEmpty(args[0])) {
            throw new FormulaException("MODULE.BUSINESS.RESULT.TIP.FORMULA_REQUIRE_ITEMNAME");
        }

        LimsResultDTO taskResult = GikamComputingDataHolder.getCurrentData();

        String itemName = args[0];
        String testCode = (args.length > 1 && !ObjectUtils.isEmpty(args[1]) ? args[1] : taskResult.getTestCode());
        String sampleCode = args.length > 2 && !ObjectUtils.isEmpty(args[2]) ? Objects.toString(args[2], taskResult.getSampleCode()) : taskResult.getSampleCode();

        List<LimsResultDTO> orderTaskResultList = GikamComputingDataHolder.get(OrderTaskResultComputingEngine.FOLDER_ORDER_TASK_RESULT_LIST);
        return orderTaskResultList.stream()
                .filter(r -> ObjectUtils.equals(itemName, r.getAnalyte()) && ObjectUtils.equals(testCode, r.getTestCode()) && ObjectUtils.equals(sampleCode, r.getSampleCode()))
                .collect(Collectors.toList());
    }

    private static LimsResultDTO getTaskResultList(String itemName) {
        if (itemName == null) {
            throw new FormulaException("MODULE.BUSINESS.RESULT.TIP.FORMULA_REQUIRE_ITEMNAME");
        }

        LimsResultDTO taskResult = GikamComputingDataHolder.getCurrentData();

        String testCode = taskResult.getTestCode();
        String sampleCode = taskResult.getSampleCode();
        Long otId = taskResult.getOrderTaskId();

        List<LimsResultDTO> orderTaskResultList = GikamComputingDataHolder.get(OrderTaskResultComputingEngine.FOLDER_ORDER_TASK_RESULT_LIST);
        return orderTaskResultList.stream()
                .filter(r -> ObjectUtils.equals(itemName, r.getAnalyte()) && ObjectUtils.equals(testCode, r.getTestCode())
                        && ObjectUtils.equals(sampleCode, r.getSampleCode()) && ObjectUtils.equals(otId, r.getOrderTaskId()))
                .findFirst().orElse(null);
    }
}
